package org.bing.zion.filter.serializer;

/*
 * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
 * Copyright (C) 2006 - Javolution (http://javolution.org/)
 * All rights reserved.
 *  
 *  Copy from Javolution.
 */

import java.io.IOException;
import java.io.Writer;
import java.nio.ByteBuffer;

import org.bing.zion.helper.DirectBufferPool;


public abstract class ByteBufferWriter extends Writer {
	protected static final DirectBufferPool bufferPool = DirectBufferPool.instance();
	protected static final SimpleReflectEncoder encoder = new SimpleReflectEncoder();
	protected ByteBuffer buffer;
	private char highSurrogate;

	public ByteBufferWriter() {
		buffer = bufferPool.obtain();
	}

	public void write(Object obj) throws IOException {
		encoder.encode(obj, this);
	}

	public void write(char c) throws IOException {
		if ((c < 0xd800) || (c > 0xdfff)) {
			write((int) c);
		} else if (c < 0xdc00) { // High surrogate.
			highSurrogate = c;
		} else { // Low surrogate.
			int code = ((highSurrogate - 0xd800) << 10) + (c - 0xdc00) + 0x10000;
			write(code);
		}
	}

	@Override
	public void write(int code) throws IOException {
		if ((code & 0xffffff80) == 0) {
			write((byte) code);
		} else { // Writes more than one byte.
			write2(code);
		}
	}

	private void write2(int c) throws IOException {
		if ((c & 0xfffff800) == 0) { // 2 bytes.
			write((byte) (0xc0 | (c >> 6)));
			write((byte) (0x80 | (c & 0x3f)));
		} else if ((c & 0xffff0000) == 0) { // 3 bytes.
			write((byte) (0xe0 | (c >> 12)));
			write((byte) (0x80 | ((c >> 6) & 0x3f)));
			write((byte) (0x80 | (c & 0x3f)));
		} else if ((c & 0xff200000) == 0) { // 4 bytes.
			write((byte) (0xf0 | (c >> 18)));
			write((byte) (0x80 | ((c >> 12) & 0x3f)));
			write((byte) (0x80 | ((c >> 6) & 0x3f)));
			write((byte) (0x80 | (c & 0x3f)));
		} else if ((c & 0xf4000000) == 0) { // 5 bytes.
			write((byte) (0xf8 | (c >> 24)));
			write((byte) (0x80 | ((c >> 18) & 0x3f)));
			write((byte) (0x80 | ((c >> 12) & 0x3f)));
			write((byte) (0x80 | ((c >> 6) & 0x3f)));
			write((byte) (0x80 | (c & 0x3f)));
		} else if ((c & 0x80000000) == 0) { // 6 bytes.
			write((byte) (0xfc | (c >> 30)));
			write((byte) (0x80 | ((c >> 24) & 0x3f)));
			write((byte) (0x80 | ((c >> 18) & 0x3f)));
			write((byte) (0x80 | ((c >> 12) & 0x3F)));
			write((byte) (0x80 | ((c >> 6) & 0x3F)));
			write((byte) (0x80 | (c & 0x3F)));
		} else {
			throw new RuntimeException("Illegal character U+" + Integer.toHexString(c));
		}
	}

	private void write(byte b) throws IOException {
		if (buffer.hasRemaining()) {
			buffer.put(b);
		} else {
			flush();
			buffer.put(b);
		}
	}

	@Override
	public void write(char cbuf[], int off, int len) throws IOException {
		final int off_plus_len = off + len;
		for (int i = off; i < off_plus_len;) {
			char c = cbuf[i++];
			if (c < 0x80) {
				write((byte) c);
			} else {
				write(c);
			}
		}
	}

	@Override
	public void write(String str, int off, int len) throws IOException {
		final int off_plus_len = off + len;
		for (int i = off; i < off_plus_len;) {
			char c = str.charAt(i++);
			if (c < 0x80) {
				write((byte) c);
			} else {
				write(c);
			}
		}
	}

	@Override
	public void flush() throws IOException {
		// do nothing, override by sub-class ? abstract
	}

	@Override
	public void close() throws IOException {
		this.flush();
		bufferPool.release(buffer);
	}

}